using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;

//数据库公用程序
namespace XunChi.DBUtility
{
    /// <summary>
    /// SqlHelper类旨在封装高性能，SqlClient常见用途的可扩展最佳实践。
    /// </summary>
    public abstract class SQLServerHelper
    {
        //数据库连接信息：获取当前应用程序默认配置的 System.Configuration.AppSettingsSection 数据。
        public static readonly string ConnectionStringLocalTransaction = ConfigurationManager.AppSettings["SQLConnString1"];//这里代码硬编码，到时候可以修改为其他字符串。
        public static readonly string ConnectionStringInventoryDistributedTransaction = ConfigurationManager.AppSettings["SQLConnString2"];
        public static readonly string ConnectionStringOrderDistributedTransaction = ConfigurationManager.AppSettings["SQLConnString3"];
        public static readonly string ConnectionStringProfile = ConfigurationManager.AppSettings["SQLProfileConnString"];

        //用于存储缓存参数的哈希表
        private static Hashtable parmCache = Hashtable.Synchronized(new Hashtable());

        /// <summary>
        /// 准备执行命令
        /// </summary>
        /// <param name="cmd">SqlCommand对象</param>
        /// <param name="conn">SqlConnection对象</param>
        /// <param name="trans">SqlTransaction对象</param>
        /// <param name="cmdType">Cmd类型，例如：存储过程或文本</param>
        /// <param name="cmdText">命令文本，例如：Select * from Products</param>
        /// <param name="cmdParms">要在命令中使用的SqlParameters</param>
        private static void PrepareCommand(SqlCommand cmd, SqlConnection conn, SqlTransaction trans, CommandType cmdType, string cmdText, SqlParameter[] cmdParms)
        {
            if (conn.State != ConnectionState.Open) //这里似乎可以改进，因为如果连接已经打开就不必要重新打开数据库连接了。
            {
                conn.Open();
            }

            cmd.Connection = conn;
            cmd.CommandText = cmdText;

            if (trans != null)
            {
                cmd.Transaction = trans;
            }

            cmd.CommandType = cmdType;

            if (cmdParms != null)
            {
                foreach (SqlParameter parm in cmdParms)
                {
                    cmd.Parameters.Add(parm);
                }
            }
        }

        /// <summary>
        /// 根据参数对连接字符串中指定的数据库执行SqlCommand（不返回结果集）
        /// </summary>
        /// <remarks>
        /// e.g.:  
        ///  int result = ExecuteNonQuery(connString, CommandType.StoredProcedure, "PublishOrders", new SqlParameter("@prodid", 24));
        /// </remarks>
        /// <param name="connectionString">SqlConnection的有效连接字符串</param>
        /// <param name="commandType">CommandType（存储过程、文本等）</param>
        /// <param name="commandText">存储过程名称或T-SQL命令</param>
        /// <param name="commandParameters">用于执行命令的SqlParamers数组</param>
        /// <returns>一个int，表示受命令影响的行数</returns>
        public static int ExecuteNonQuery(string connectionString, CommandType cmdType, string cmdText, params SqlParameter[] commandParameters)
        {
            SqlCommand cmd = new SqlCommand();

            using (SqlConnection conn = new SqlConnection(connectionString))
            {
                PrepareCommand(cmd, conn, null, cmdType, cmdText, commandParameters);
                int affectedRows = cmd.ExecuteNonQuery();//对连接执行 Transact-SQL 语句并返回受影响的行数。
                cmd.Parameters.Clear();//清空Transact-SQL 语句或存储过程的参数。
                return affectedRows;
            }
        }

        /// <summary>
        /// 根据参数返回批量更新的记录数。
        /// </summary>
        /// <returns></returns>
        public int BatchUpdate()
        {
            int batchCount = 0;
            return batchCount;
        }


        /// <summary>
        /// 使用所提供的参数。对现有数据库连接执行SqlCommand（不返回结果集）
        /// </summary>
        /// <remarks>
        /// 例如：
        ///  int result = ExecuteNonQuery(connString, CommandType.StoredProcedure, "PublishOrders", new SqlParameter("@prodid", 24));
        /// </remarks>
        /// <param name="conn">现有数据库连接</param>
        /// <param name="commandType">CommandType（存储过程、文本等）</param>
        /// <param name="commandText">存储过程名称或T-SQL命令</param>
        /// <param name="commandParameters">用于执行命令的SqlParamers数组</param>
        /// <returns>一个int，表示受命令影响的行数</returns>
        public static int ExecuteNonQuery(SqlConnection connection, CommandType cmdType, string cmdText, params SqlParameter[] commandParameters)
        {
            SqlCommand cmd = new SqlCommand();

            PrepareCommand(cmd, connection, null, cmdType, cmdText, commandParameters);
            int affectedRows = cmd.ExecuteNonQuery();
            cmd.Parameters.Clear();
            return affectedRows;
        }

        /// <summary>
        /// 使用所提供的参数使用现有的SQL事务执行SqlCommand（不返回结果集) 
        /// </summary>
        /// <remarks>
        /// 例如： 
        ///  int result = ExecuteNonQuery(connString, CommandType.StoredProcedure, "PublishOrders", new SqlParameter("@prodid", 24));
        /// </remarks>
        /// <param name="trans">一个现有的sql事务</param>
        /// <param name="commandType">CommandType（存储过程、文本等）</param>
        /// <param name="commandText">存储过程名称或T-SQL命令</param>
        /// <param name="commandParameters">用于执行命令的SqlParamers数组</param>
        /// <returns>一个int，表示受命令影响的行数：</returns>
        public static int ExecuteNonQuery(SqlTransaction trans, CommandType cmdType, string cmdText, params SqlParameter[] commandParameters)
        {
            SqlCommand cmd = new SqlCommand();
            PrepareCommand(cmd, trans.Connection, trans, cmdType, cmdText, commandParameters);
            int affectedRows = cmd.ExecuteNonQuery();
            cmd.Parameters.Clear();
            return affectedRows;
        }

        /// <summary>
        /// 使用所提供的参数执行SqlCommand，返回连接字符串中指定的数据库的结果集
        /// </summary>
        /// <remarks>
        /// 例如：
        ///  SqlDataReader r = ExecuteReader(connString, CommandType.StoredProcedure, "PublishOrders", new SqlParameter("@prodid", 24));
        /// </remarks>
        /// <param name="connectionString">SqlConnection的有效连接字符串</param>
        /// <param name="commandType">CommandType(存储过程、文本等)</param>
        /// <param name="commandText">存储过程名称或T-SQL命令</param>
        /// <param name="commandParameters">用于执行命令的SqlParamers数组</param>
        /// <returns>包含结果的SqlDataReader</returns>
        public static SqlDataReader ExecuteReader(string connectionString, CommandType cmdType, string cmdText, params SqlParameter[] commandParameters)
        {
            SqlCommand cmd = new SqlCommand();
            SqlConnection con = new SqlConnection(connectionString);

            //我们在这里使用try/catch，因为如果方法抛出异常，我们希望关闭连接抛出代码，因为不存在数据读取器，因此指挥行为。CloseConnection不起作用
            try
            {
                PrepareCommand(cmd, con, null, cmdType, cmdText, commandParameters);
                SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
                cmd.Parameters.Clear();
                return dr;
            }
            catch
            {
                con.Close();
                throw;
            }
        }

        /// <summary>
        /// 使用所提供的参数，执行SqlCommand，返回连接字符串中指定的数据库中第一条记录的第一列的值。
        /// </summary>
        /// <remarks>
        /// 例如：
        ///  Object obj = ExecuteScalar(connString, CommandType.StoredProcedure, "PublishOrders", new SqlParameter("@prodid", 24));
        /// </remarks>
        /// <param name="connectionString">SqlConnection的有效连接字符串</param>
        /// <param name="commandType">CommandType（存储过程、文本等）</param>
        /// <param name="commandText">存储过程名称或T-SQL命令</param>
        /// <param name="commandParameters">用于执行命令的SqlParamers数组</param>
        /// <returns>应使用Convert将其转换为预期类型的对象。致｛类型}</returns>
        public static object ExecuteScalar(string connectionString, CommandType cmdType, string cmdText, params SqlParameter[] commandParameters)
        {
            SqlCommand cmd = new SqlCommand();

            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                PrepareCommand(cmd, connection, null, cmdType, cmdText, commandParameters);
                object obj = cmd.ExecuteScalar();
                cmd.Parameters.Clear();
                return obj;
            }
        }

        /// <summary>
        /// 使用所提供的参数:对现有数据库连接执行SqlCommand，返回第一条记录的第一列
        /// </summary>
        /// <remarks>
        /// 例如：
        ///  Object obj = ExecuteScalar(connString, CommandType.StoredProcedure, "PublishOrders", new SqlParameter("@prodid", 24));
        /// </remarks>
        /// <param name="conn">现有数据库连接</param>
        /// <param name="commandType">CommandType（存储过程、文本等）</param>
        /// <param name="commandText">存储过程名称或T-SQL命令</param>
        /// <param name="commandParameters">用于执行命令的SqlParamers数组</param>
        /// <returns>应使用Convert.To(Type)将其转换为预期类型的对象。</returns>
        public static object ExecuteScalar(SqlConnection connection, CommandType cmdType, string cmdText, params SqlParameter[] commandParameters)
        {
            SqlCommand cmd = new SqlCommand();

            PrepareCommand(cmd, connection, null, cmdType, cmdText, commandParameters);
            object obj = cmd.ExecuteScalar();//为结果集中的第一行的第一列，或者，如果结果集为空，则为 null 引用
            cmd.Parameters.Clear();
            return obj;
        }

        #region 参数缓存到字典中去

        /// <summary>
        /// 将参数数组添加到缓存中
        /// </summary>
        /// <param name="cacheKey">参数缓存密钥</param>
        /// <param name="cmdParms">要缓存的SqlParamers数组</param>
        public static void CacheParameters(string cacheKey, params SqlParameter[] commandParameters)
        {
            parmCache[cacheKey] = commandParameters;
        }

        /// <summary>
        /// 检索缓存参数
        /// </summary>
        /// <param name="cacheKey">用于查找参数的键</param>
        /// <returns>缓存的SqlParamers数组</returns>
        public static SqlParameter[] GetCachedParameters(string cacheKey)
        {
            SqlParameter[] cachedParms = (SqlParameter[])parmCache[cacheKey];

            if (cachedParms == null)
            {
                return null;
            }

            SqlParameter[] clonedParms = new SqlParameter[cachedParms.Length];

            for (int i = 0, j = cachedParms.Length; i < j; i++)
            {
                clonedParms[i] = (SqlParameter)((ICloneable)cachedParms[i]).Clone();
            }

            return clonedParms;
        }

        #endregion
    }
}